package com.anlogic.sdk.bsp.settings;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITableColorProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Table;

import com.anlogic.sdk.tools.model.DriverInfo;

public class DriversView {
	BspSettingsView parentView;
	
	private IBspSettingTool bspSettingTool;

	private TableViewer driversViewer;

	private Composite content;

	private TableViewerColumn[] driversViewerColumn;

	private String[] driverHeaders = new String[] { "Component", "ComponentType", "DriverName", "DriverVersion" };

	private int[] driverHeaderWidths = new int[] { 200, 200, 200, 100 };

	private List<DriverAssignment> driverAssignments;

	private DriverNameEditingSupport nameEditSupport;

	private DriverVersionEditingSupport versionEditSupport;

	public DriversView(BspSettingsView parentView, Composite parent, IBspSettingTool bspSettingTool) {
		this.parentView = parentView;
		this.bspSettingTool = bspSettingTool;
		this.createContentArea(parent);
	}

	public void setDriversContentProvider(IStructuredContentProvider p) {
		this.driversViewer.setContentProvider((IContentProvider) p);
	}

	public void setDriversLabelProvider(ITableLabelProvider p) {
		this.driversViewer.setLabelProvider((IBaseLabelProvider) p);
	}

	public void refreshDrivers(Object input) {
		this.driversViewer.setInput(input);
	}

	public TableViewer getDriversViewer() {
		return this.driversViewer;
	}

	public void setNameEditingSupport(EditingSupport es) {
		TableViewerColumn vc = this.driversViewerColumn[2];
		vc.setEditingSupport(es);
	}

	public void setVersionEditingSupport(EditingSupport es) {
		TableViewerColumn vc = this.driversViewerColumn[3];
		vc.setEditingSupport(es);
	}

	public Display getDisplay() {
		return this.content.getDisplay();
	}

	public Composite getContent() {
		return this.content;
	}
	
	private void createContentArea(Composite parent) {
		this.content = new Composite(parent, SWT.NONE);
		this.content.setLayout((Layout) new GridLayout(1, false));
		this.content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		
		createDriverViewer(this.content);
	}
	
	private void createDriverViewer(Composite parent) {
		Label info = new Label(parent, SWT.WRAP);
		info.setText("Modify the driver or its version assigned for each component. If you do not want to assign a driver to a component or peripheral, please choose 'none'.");
		this.driversViewer = new TableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.SINGLE);
		this.driversViewer.setUseHashlookup(true);
		this.driversViewerColumn = new TableViewerColumn[this.driverHeaders.length];
		for (int i = 0; i < this.driverHeaders.length; i++) {
			this.driversViewerColumn[i] = new TableViewerColumn(this.driversViewer, SWT.FILL);
			this.driversViewerColumn[i].getColumn().setText(this.driverHeaders[i]);
			this.driversViewerColumn[i].getColumn().setWidth(this.driverHeaderWidths[i]);
			this.driversViewerColumn[i].getColumn().setResizable(true);
			this.driversViewerColumn[i].getColumn().setMoveable(false);
		}
		Table table = this.driversViewer.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		table.setLayout((Layout) new GridLayout(1, false));
		GridData td = new GridData(SWT.FILL, SWT.TOP, true, true);
		td.verticalIndent = 15;
		td.horizontalIndent = 5;
		table.setLayoutData(td);
	}

	public void viewReady() {
		DriversManager drvman = new DriversManager();
		createDriverAssignment();
		this.versionEditSupport = new DriverVersionEditingSupport((ColumnViewer) this.getDriversViewer());
		this.nameEditSupport = new DriverNameEditingSupport((ColumnViewer) this.getDriversViewer());
		this.setDriversContentProvider(drvman);
		this.setDriversLabelProvider(drvman);
		this.setNameEditingSupport(this.nameEditSupport);
		this.setVersionEditingSupport(this.versionEditSupport);
		this.refreshDrivers("");
	}

	private void createDriverAssignment() {
		this.driverAssignments = new ArrayList<DriverAssignment>();
		Map<String, Map<String, DriverInfo>> driverMap = this.bspSettingTool.getDriverInfo();
		Map<String, DriverInfo> bspDriverMap = this.bspSettingTool.getBspDriverInfo();
		for (String name : driverMap.keySet()) {
			String[] versions = driverMap.get(name).keySet().toArray(new String[0]);
			
			String lowCaseName = name.toLowerCase();
			DriverAssignment da = new DriverAssignment();
			da.setHwInstanceName(lowCaseName);
			da.setHwInstanceType(lowCaseName + "_v" + versions[0].replace('.', '_'));
			da.addDriver("none", versions[0]);
			da.addDriver(lowCaseName, versions);
			if (bspDriverMap.containsKey(name)) {
				da.setSelectedDriver(lowCaseName, bspDriverMap.get(name).getVersion());
			} else {
				da.setSelectedDriver("none", versions[0]);
			}
			this.driverAssignments.add(da);
		}
	}

	class DriversManager extends LabelProvider
			implements IStructuredContentProvider, ITableLabelProvider, ITableColorProvider {
		private Color gray = new Color(null, 150, 150, 150);

		private Color red = new Color(null, 200, 0, 0);

		public Object[] getElements(Object inputElement) {
			return driverAssignments.toArray();
		}

		public void dispose() {
		}

		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}

		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}

		public String getColumnText(Object element, int columnIndex) {
			String type, ver;
			if (!(element instanceof DriverAssignment))
				return "";
			DriverAssignment da = (DriverAssignment) element;
			switch (columnIndex) {
			case 0:
				return da.getHwInstanceName();
			case 1:
				type = da.getHwInstanceType();
				ver = type.substring(type.length() - 8);
				if (ver.matches(".*_[v][0-9]_[0-9][0-9]_[a-z]"))
					return type.substring(0, type.length() - 8);
				return type.replaceAll("_[v][0-9]_.*", "");
			case 2:
				return da.getSelectedDriver().getName();
			case 3:
				return da.getSelectedDriver().getSelectedVersionString();
			}
			return "";
		}

		public Color getBackground(Object element, int columnIndex) {
			return null;
		}

		public Color getForeground(Object element, int columnIndex) {
			if (!(element instanceof DriverAssignment))
				return null;
			DriverAssignment da = (DriverAssignment) element;
			switch (columnIndex) {
			case 0:
			case 1:
				return this.gray;
			case 2:
				if (da.getSelectedDriver().getName().equals("none"))
					return this.red;
				return null;
			}
			return null;
		}
	}

	class DriverVersionEditingSupport extends EditingSupport {
		private ComboBoxCellEditor[] cellEditors;

		private ColumnViewer viewer;

		public DriverVersionEditingSupport(ColumnViewer viewer) {
			super(viewer);
			TableViewer tv = (TableViewer) viewer;
			Table table = tv.getTable();
			this.cellEditors = new ComboBoxCellEditor[driverAssignments.size()];
			for (int i = 0; i < this.cellEditors.length; i++) {
				DriverAssignment da = driverAssignments.get(i);
				Component s = da.getSelectedDriver();
				this.cellEditors[i] = new ComboBoxCellEditor((Composite) table,
						s.getVersions().<String>toArray(new String[0]), 8);
			}
			this.viewer = viewer;
		}

		protected boolean canEdit(Object element) {
			return true;
		}

		protected CellEditor getCellEditor(Object element) {
			if (!(element instanceof DriverAssignment))
				return null;
			DriverAssignment da = (DriverAssignment) element;
			int index = driverAssignments.indexOf(da);
			if (index == -1)
				return null;
			return (CellEditor) this.cellEditors[index];
		}

		protected Object getValue(Object element) {
			if (!(element instanceof DriverAssignment))
				return null;
			DriverAssignment da = (DriverAssignment) element;
			Component d = da.getSelectedDriver();
			return d.getSelectedVersion();
		}

		protected void setValue(Object element, Object value) {
			if (!(element instanceof DriverAssignment))
				return;
			DriverAssignment da = (DriverAssignment) element;
			Component d = da.getSelectedDriver();
			Integer i = (Integer) value;
			if (d.getSelectedVersion() != i.intValue()) {
				d.setSelectedVersion(i.intValue());
				List<DriverAssignment> updated = updateSelectedDriverVersion(d);
				bspSettingTool.setDriverVersion(d.getName(), d.getSelectedVersionString());
				for (DriverAssignment a : updated)
					this.viewer.refresh(a);
			}
		}

		private List<DriverAssignment> updateSelectedDriverVersion(Component driver) {
			List<DriverAssignment> updated = new ArrayList<DriverAssignment>();
			Iterator<DriverAssignment> itr = driverAssignments.iterator();
			while (itr.hasNext()) {
				DriverAssignment da = itr.next();
				if (da.getSelectedDriver().getName().equals(driver.getName())) {
					da.getSelectedDriver().setSelectedVersion(driver.getSelectedVersion());
					updated.add(da);
				}
			}
			return updated;
		}

		public void setCellEditorItems(DriverAssignment da, String[] items) {
			int index = driverAssignments.indexOf(da);
			if (index == -1)
				return;
			this.cellEditors[index].setItems(items);
			this.viewer.refresh(da);
		}
	}

	class DriverNameEditingSupport extends EditingSupport {
		private ComboBoxCellEditor[] cellEditors;

		private ColumnViewer viewer;

		public DriverNameEditingSupport(ColumnViewer viewer) {
			super(viewer);
			TableViewer tv = (TableViewer) viewer;
			Table t = tv.getTable();
			this.cellEditors = new ComboBoxCellEditor[driverAssignments.size()];
			for (int i = 0; i < this.cellEditors.length; i++) {
				DriverAssignment da = driverAssignments.get(i);
				this.cellEditors[i] = new ComboBoxCellEditor((Composite) t, da.getDriverNames(), 8);
			}
			this.viewer = viewer;
		}

		protected boolean canEdit(Object element) {
			return true;
		}

		protected CellEditor getCellEditor(Object element) {
			if (!(element instanceof DriverAssignment))
				return null;
			DriverAssignment da = (DriverAssignment) element;
			int index = driverAssignments.indexOf(da);
			if (index == -1)
				return null;
			return (CellEditor) this.cellEditors[index];
		}

		protected Object getValue(Object element) {
			if (!(element instanceof DriverAssignment))
				return null;
			DriverAssignment da = (DriverAssignment) element;
			return da.getSelectedDriverIndex();
		}

		protected void setValue(Object element, Object value) {
			if (!(element instanceof DriverAssignment))
				return;
			DriverAssignment da = (DriverAssignment) element;
			int index = (Integer) value;
			if (da.getSelectedDriverIndex() != index) {
				Component oldDriver = da.getSelectedDriver();
				da.setSelectedDriverIndex(index);
				Component newDriver = da.getSelectedDriver();
				versionEditSupport.setCellEditorItems(da, newDriver.getVersions().toArray(new String[0]));
				da.setSelectedDriverIndex(index);
				if (newDriver.getName().equals("none")) {
					bspSettingTool.removeDriver(oldDriver.getName().toUpperCase());
				} else {
					bspSettingTool.setDriverVersion(newDriver.getName().toUpperCase(), newDriver.getSelectedVersionString());
				}
			}
			this.viewer.refresh(da);
		}
	}
}
